/*
 * Copyright (c) 2011 Anthony Vandikas
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation 
 * files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, 
 * modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software 
 * is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 
 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include "stdafx.h"
#include "HistoryModel.h"

#include "AbstractHistory.h"

HistoryModel::HistoryModel(AbstractHistory &history, HistoryDisplayFlags flags)
	: history(&history),
	// Show all entries for today as default.
	minDate(QDate::currentDate()),
	maxDate(QDate::currentDate()),
	studentKeywords(QStringList()),
	itemKeywords(QStringList())
{
	// These blocks test each possible flag, and add them to the columns.
	if (flags.testFlag(DateColumn) && flags.testFlag(TimeColumn))
		columns << (DateColumn | TimeColumn);
	else if (flags.testFlag(DateColumn))
		columns << DateColumn;
	else if (flags.testFlag(TimeColumn))
		columns << TimeColumn;

	if (flags.testFlag(StudentColumn))
		columns << StudentColumn;
	if (flags.testFlag(ItemColumn))
		columns << ItemColumn;

	// By connecting the modified() signal to the refresh() slot,
	// the model will be updated every time the history database
	// is changed.
	connect(&history, SIGNAL(modified()), this, SLOT(refresh()));

	// Sort the first column by default.
	// This needs to be here or nothing will show up until the database is updated,
	// or the list is sorted or filtered.
	sort(0, Qt::AscendingOrder);
}

QModelIndex HistoryModel::index(int row, int column, const QModelIndex &parent) const
{
	// The model doesn't have any cases where indices are parented,
	// so don't return a valid index if the user somehow passes in 
	// a valid parent index.
	return parent.isValid() ? QModelIndex() : createIndex(row, column);
}

QModelIndex HistoryModel::parent(const QModelIndex &child) const
{
	return QModelIndex();
}

int HistoryModel::rowCount(const QModelIndex &parent) const
{
	return parent.isValid() ? 0 : entries.size();
}

int HistoryModel::columnCount(const QModelIndex &parent) const
{
	return parent.isValid() ? 0 : columns.size();
}

QVariant HistoryModel::data(const QModelIndex &index, int role) const
{
	// We don't care about any type of data except that which is being displayed.
	if (role != Qt::DisplayRole)
		return QVariant();

	auto entry = entries[index.row()];
	auto column = columns[index.column()];

	switch (column)
	{
		// The compiler throws up if '|' is used, BUT
		// since all the values are powers of two,
		// '+' produces the same result as '|'.
	case DateColumn + TimeColumn:
		return entry.getDateTime();
	case DateColumn:
		return entry.getDate();
	case TimeColumn:
		return entry.getTime();
	case StudentColumn:
		return entry.getStudent().toString();
	case ItemColumn:
		return entry.getItem().toString();
	default: // Shouldn't happen.
		return QVariant();
	}
}

QVariant HistoryModel::headerData(int section, Qt::Orientation orientation, int role) const
{
	// We don't care about any type of data except that which is being displayed.
	if (role != Qt::DisplayRole)
		return QVariant();

	switch (columns[section])
	{
	case DateColumn + TimeColumn:
		return "Date/Time";
	case DateColumn:
		return "Date";
	case TimeColumn:
		return "Time";
	case StudentColumn:
		return "Student";
	case ItemColumn:
		return "Item";
	default:
		return QVariant();
	}
}

void HistoryModel::sort(int column, Qt::SortOrder order)
{
	// Sorting is done using the qSort function (provided by QT)
	// which can take in a function defining how to compare elements.
	// Whenever this function is called, the comparison function is dynamically
	// redefined to fit the parameters (column/order) via a lambda function.
	sortComparisonFunc = [=](const Entry &left, const Entry &right) -> bool
	{
		bool output;

		// I have to cast to int, or the compiler complains.
		// I'm guessing it's a bug with lambda expressions.
		switch ((int)columns[column])
		{
		case DateColumn + TimeColumn:
		case DateColumn:
		case TimeColumn:
			output = left.getDateTime() < right.getDateTime();
			break;
		case StudentColumn:
			output = left.getStudent().getName() < right.getStudent().getName();
			break;
		case ItemColumn:
			// Sort by name, then by number.
			if (left.getItem().getName() == right.getItem().getName())
				output = left.getItem().getNumber() < right.getItem().getNumber();
			else
				output = left.getItem().getName() < right.getItem().getName();
			break;
		default:
			output = false;
		}

		return order == Qt::AscendingOrder ? output : !output;
	};

	refresh();
}

void HistoryModel::filter(const QDate &minDate, const QDate &maxDate, const QStringList &studentKeywords, const QStringList &itemKeywords)
{
	this->minDate = minDate;
	this->maxDate = maxDate;
	this->studentKeywords = studentKeywords;
	this->itemKeywords = itemKeywords;

	refresh();
}

void HistoryModel::refresh() 
{
	entries = history->filter(minDate, maxDate, studentKeywords, itemKeywords);

	qSort(entries.begin(), entries.end(), sortComparisonFunc);
	reset();
}
